from django.conf import settings
from django.db import transaction
from django.db.models import Max
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer

from apps.tcpserver.proto.serializers import *
from tcpmanage.manager import TCPManager
from util.typing import RestFrameWorkModelSerializerTyping
from .models import *
from apps.common.models import Region

from apps.log.models import UserActionLog


class DevicesSensorSeriallizer(serializers.Serializer):

    id = serializers.IntegerField()
    icon = serializers.CharField()
    name = serializers.CharField()
    unit = serializers.CharField()

    data = serializers.SerializerMethodField()

    def get_data(self,row):
        kwargs = dict()
        if type(row) == Sensor:
            kwargs['sensor_id'] = row.id
        elif type(row) == MockSensor:
            kwargs['mock_sensor_id'] = row.id
        else:
            kwargs['number_sensor_id'] = row.id
        his = History.objects.filter(**kwargs).order_by('-time_ticket').first()
        return {
            'data': his.data if his else 0
        }
        






class DevicesModelSerializer(ModelSerializer):
    status = serializers.SerializerMethodField()
    region_id = serializers.IntegerField(write_only=True)
    users_id = serializers.SerializerMethodField()

    sensor = serializers.SerializerMethodField()

    def get_sensor(self, row):

        request = self.context['request']
        ua = request.headers.get("User-Agent", "")
        is_wechat = ua.find("MicroMessenger") >= 0
        if is_wechat:
            sensors = []
            sensors += Sensor.objects.filter(
                devices_id=row.id
            ).all()
            sensors += MockSensor.objects.filter(
                devices_id=row.id
            ).all()
            sensors += NumberSensor.objects.filter(
                devices_id=row.id
            ).all()
            
            res = DevicesSensorSeriallizer(instance=sensors,many=True).data

        else:
            res = []

        return res

    def get_users_id(self, row: Devices):
        request = self.context['request']
        if request.user.is_superuser:
            return [item['id'] for item in row.user.values('id')]
        return [item['id'] for item in row.user.filter(parent_user=request.user).values('id')]

    def get_status(self, row):
        return bool(row.status)

    def update(self, instance: Devices, validated_data):
        request = self.context['request']

        region = Region.objects.get(pk=request.data.get('region'))
        if(instance.region):
            regions = instance.region.filter(user_id=request.user.id).all()
            instance.region.remove(*regions)

        instance.region.add(region)
        return super().update(instance, validated_data)

    class Meta(RestFrameWorkModelSerializerTyping):
        model = Devices
        exclude = ('user',)
        depth = 2
        read_only_fields = [
            'id',
            'hardware_model',
            'manufacture_tmie',
            'hardware_version',
            'software_version',
            'device_serial_number',
        ]


class ControllersSerializer(Serializer):
    effective = IntegerField(
        length=1,
        order=1,
        doc='起始标志,标志该控制器有效无效'
    )
    number = IntegerField(
        length=1,
        bytesorder='little',
        order=2,
        doc='编号'
    )
    status = IntegerField(
        length=1,
        order=3,
        doc='是否启用'
    )
    mode = IntegerField(
        length=1,
        order=4,
        doc='控制模式'
    )
    default_value = IntegerField(

        length=4,
        bytesorder='little',
        order=5,
        doc='初始默认值',
        signed=False
    )


class ControlSerialzer(ModelSerializer):
    inline_status = serializers.SerializerMethodField()
    default_value = serializers.IntegerField(
        max_value=1,
        min_value=0,
        error_messages={
            'max_value': '最大值为1',
            'min_value': '最小值为0'
        }
    )

    def get_inline_status(self, row):
        for i in self.context['controls_status'].items():
            print(i)
        try:
            return self.context['controls_status'].get(row.number)
        except Exception as e:
            return {}

    class Meta:
        model = Controls
        fields = '__all__'
        read_only_fields = ['devices', 'id', 'u_id', 'number']

    def update(self, instance, valid_data):
        instances = Controls.objects.filter(
            devices_id=instance.devices_id).order_by('id')
        # 排序位置，方便更改
        index = list(instances).index(instance)
        # 发送offer更改
        valid_data['number'] = str(index + 1)
        res = TCPManager.get_instance().offer(instance.devices.device_serial_number, msg={
            'item': 'number_output',
            'subitem': 'settings',
            'data': [valid_data],
            'index': index
        }, handle_type=3, time_out=settings.TASK_TIME_OUT)
        if res:
            return super().update(instance, valid_data)
        raise Exception('update fail')


class SensorSerialzer(ModelSerializer):
    name = serializers.CharField()
    data = serializers.SerializerMethodField()

    type = serializers.SerializerMethodField()

    def get_type(self, row):
        return '485'

    def get_data(self, row: Sensor):
        request = self.context['request']
        limit = request.query_params.get("limit", '20')
        limit = int(limit)
        query = History.objects.filter(
            sensor_id=row.id).order_by('time_ticket').all()
        print(query.count() - limit)
        if query.count() >= limit:
            data = query[query.count() - limit:]
        else:
            data = query
        return HistorySerialzer(instance=data, many=True, read_only=True).data

    class Meta(RestFrameWorkModelSerializerTyping):
        model = Sensor
        fields = '__all__'
        depth = 0
        read_only_fields = ['devices', 'id', 'input_number', 'status', 'u_id']

    def update(self, instanct: Sensor, data):

        index = instanct.input_number - 1

        # 发送offer更改传感器
        data['input_number'] = instanct.input_number
        data['status'] = instanct.status
        res = TCPManager.get_instance().offer(instanct.devices.device_serial_number, msg={
            'item': 'sensor_485',
            'subitem': 'settings',
            'data': [data],
            'index': index
        }, handle_type=3, time_out=settings.TASK_TIME_OUT)
        if res:
            return super().update(instanct, data)
        raise Exception('update fail')

    def create(self, validate_data):
        dev = self.context['dev']
        request = self.context['request']
        instancts = Sensor.objects.filter(
            devices_id=dev.id
        )
        max_number = instancts.aggregate(max=Max('input_number'))['max']
        # 查询所有number
        numbers = instancts.values('input_number')
        toatl = self._get_not_use_number(max_number, numbers)
        # 发送offer更改传感器
        validate_data['input_number'] = toatl + 1
        validate_data['status'] = 1
        validate_data['devices_id'] = dev.id
        validate_data['u_id'] = toatl + 1
        res = TCPManager.get_instance().offer(
            dev.device_serial_number,
            msg={
                'item': 'sensor_485',
                'subitem': 'settings',
                'data': [validate_data],
                'index': toatl
            },
            handle_type=3,
            time_out=settings.TASK_TIME_OUT
        )
        if res:
            instance = super().create(validate_data)

            UserActionLog.objects.create(
                user=request.user,
                devices=dev,
                description=f"新增了485传感器{instance.name}"
            )

            return instance
        raise Exception('update fail')

    def _get_not_use_number(self, max_number: int, numbers: dict) -> int:
        """
        获取设备当中  自动规则未使用的number

        :param max_number:
        :param numbers:
        :return:
        """
        count = 0
        if len(numbers) == max_number:
            # 假如number中间没有空出的的number则立即返回
            count = max_number
        else:
            for i in numbers:

                """
                    从零开始遍历 number 假如中间删除了一个  
                    例如  numbers 为 [1,2,3,4,7]
                    则当count为4的时候就会跳出，拿到应该使用的number，  
                    后期优化可以使用set 中的 set.difference() 来查找
                """
                if i['input_number'] - 1 != count:
                    break

                count += 1

        return count


class HistorySerialzer(ModelSerializer):

    class HistorySensor(serializers.Serializer):
        id = serializers.IntegerField()
        devices = serializers.IntegerField(source="devices_id")
        input_number = serializers.IntegerField()
        name = serializers.CharField()

    devices_name = serializers.CharField(source="devices.alias")
    devices_id = serializers.CharField(source="devices.id")
    sensor_name = serializers.SerializerMethodField()

    def get_sensor_name(self, row: History):
        if row.sensor:
            return row.sensor.name

        if row.number_sensor:
            return row.number_sensor.name

        if row.mock_sensor:
            return row.mock_sensor.name

        return ''

    class Meta(RestFrameWorkModelSerializerTyping):
        model = History
        # fields = '__all__'
        depth = 1
        exclude = ('devices', 'mock_sensor', 'sensor',
                   'number_sensor', 'data_format', 'collection')


class AutoRuleSerializer(ModelSerializer):
    class Meta(RestFrameWorkModelSerializerTyping):

        exclude = ('devices',)
        model = AutoRule
        read_only_fields = ('u_id', 'devices', 'rule_number')

    def update(self, instance: AutoRule, valid_data: dict):

        index = instance.rule_number
        # 发送offer更改
        valid_data['rule_number'] = index
        valid_data['id'] = instance.id
        res = TCPManager.get_instance().offer(
            instance.devices.device_serial_number,
            msg={
                'item': 'auto_rule',
                'subitem': 'settings',
                'data': [valid_data],
                'index': index - 1
            },
            handle_type=3,
            time_out=settings.TASK_TIME_OUT
        )
        if res:
            request = self.context['request']
            UserActionLog.objects.create(
                user=request.user,
                devices=instance.devices,
                description=f"修改了自动规则{instance.name}"
            )
            return super().update(instance, valid_data)
        raise Exception('update fail')

    def create(self, validated_data):
        instance = self.context['dev']
        request = self.context['request']

        with transaction.atomic() as tr:
            """
                加锁 开启事务 防止多个请求时计算自动规则 number的时候计算重复，导致设备number重复，自动规则失效
            """
            queryset = AutoRule.objects.select_for_update().filter(devices_id=instance.id)
            # 查询最大的number
            max_number = queryset.aggregate(max=Max('rule_number'))['max']
            # 查询所有number
            numbers = queryset.values('rule_number')
            count = self._get_not_use_number(max_number, numbers)
            validated_data['rule_number'] = count + 1
            validated_data['u_id'] = count + 1
            validated_data['devices_id'] = instance.id
            res = TCPManager.get_instance().offer(
                instance.device_serial_number,
                msg={
                    'item': 'auto_rule',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': count
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )
            if res:
                instance = super().create(validated_data)
                UserActionLog.objects.create(
                    user=request.user,
                    devices=instance.devices,
                    description=f"新建了自动规则{instance.name}"
                )
                return instance
            raise Exception('create fail')

    def _get_not_use_number(self, max_number: int, numbers: dict) -> int:
        """
        获取设备当中  自动规则未使用的number

        :param max_number:
        :param numbers:
        :return:
        """
        count = 0
        if len(numbers) == max_number:
            # 假如number中间没有空出的的number则立即返回
            count = max_number
        else:
            for i in numbers:

                """
                    从零开始遍历 number 假如中间删除了一个  
                    例如  numbers 为 [1,2,3,4,7]
                    则当count为4的时候就会跳出，拿到应该使用的number，  
                    后期优化可以使用set 中的 set.difference() 来查找
                """
                if i['rule_number'] - 1 != count:
                    break

                count += 1

        return count


class ControlGroupSerializer(ModelSerializer):
    """
    组合控制
    """
    inline_status = serializers.SerializerMethodField()

    def get_inline_status(self, row):
        try:
            return self.context['controls_status'].get(row.number)
        except Exception as e:
            return {}

    class Meta(RestFrameWorkModelSerializerTyping):
        fields = '__all__'
        model = ControlGroup
        read_only_fields = ('u_id', 'devices', 'is_use', 'number')

    def update(self, instanct: ControlGroup, validate_data: dict):
        # 获取当前排序位置，方便更改
        index = instanct.number
        # 发送offer更改传感器
        request = self.context['request']
        validate_data['number'] = instanct.number
        validate_data['is_use'] = 1
        if request.data.get('mode') == 0:
            closed = AutoRule.objects.filter(
                output_type=3,
                output_channle=instanct.number,
                rule_code=1,
                devices_id=instanct.devices.id
            ).all()
        elif request.data.get('mode') == 1:
            closed = AutoRule.objects.filter(
                output_type=3,
                output_channle=instanct.number,
                rule_code=1,
                devices_id=instanct.devices.id
            ).exclude(
                time_status=1
            ).all()

        else:
            closed = AutoRule.objects.filter(
                output_type=3,
                output_channle=instanct.number,
                rule_code=1,
                devices_id=instanct.devices.id
            ).exclude(
                time_status=2
            ).all()

        for i in closed:
            data = AutoRuleSerializer(
                instance=i,
                context={'request': request, 'dev': instanct.devices}
            ).data
            # 修改status 标记设备ROM段中该串空间可用  并关闭自动规则
            data['rule_code'] = 0
            # 发布事件
            resp = TCPManager.get_instance().offer(
                instanct.devices.device_serial_number,
                {
                    'item': 'auto_rule',
                    'subitem': 'settings',
                    'data': [data],
                    'index': int(i.rule_number) - 1
                },
                handle_type=3,
                is_callback=True
            )
            i.rule_code = 0
            i.save()

    

        res = TCPManager.get_instance().offer(
            instanct.devices.device_serial_number,
            msg={
                'item': 'number_group_output',
                'subitem': 'settings',
                'data': [validate_data],
                'index': index - 1
            },
            handle_type=3,
            time_out=settings.TASK_TIME_OUT
        )
        if not res['msg']['data']:
            raise Exception('update faild')
        request = self.context['request']
        UserActionLog.objects.create(
            user=request.user,
            devices=instanct.devices,
            description=f"修改了组合控制{instanct.alias}"
        )
        return super().update(instanct, validate_data)

    def create(self, validated_data):
        instance: Devices = self.context['dev']
        with transaction.atomic() as tr:
            """
                加锁 开启事务 防止多个请求时计算 number的时候计算重复，导致设备number重复，自动规则失效
            """
            queryset = ControlGroup.objects.select_for_update().filter(devices_id=instance.id)
            # 查询最大的number
            max_number = queryset.aggregate(max=Max('number'))['max']
            # 查询所有number
            numbers = queryset.values('number')
            count = self._get_not_use_number(max_number, numbers)
            validated_data['number'] = count + 1
            validated_data['u_id'] = count + 1
            validated_data['is_use'] = 1
            validated_data['devices_id'] = instance.id
            res = TCPManager.get_instance().offer(
                instance.device_serial_number,
                msg={
                    'item': 'number_group_output',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': count
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )

            if res:
                instance = super().create(validated_data)
                request = self.context['request']
                UserActionLog.objects.create(
                    user=request.user,
                    devices=instance.devices,
                    description=f"新建了组合控制{instance.alias}"
                )
                return instance

            raise Exception('update fail')

    def _get_not_use_number(self, max_number: int, numbers: dict) -> int:
        """
        获取设备当中  未使用的number

        :param max_number:
        :param numbers:
        :return:
        """
        count = 0
        if len(numbers) == max_number:
            # 假如number中间没有空出的的number则立即返回
            count = max_number
        else:
            for i in numbers:

                """
                    从零开始遍历 number 假如中间删除了一个  
                    例如  numbers 为 [1,2,3,4,7]
                    则当count为4的时候就会跳出，拿到应该使用的number，  
                    后期优化可以使用set 中的 set.difference() 来查找
                """
                if i['number'] - 1 != count:
                    break

                count += 1

        return count


class MockOutputSerializer(ModelSerializer):
    """

    模拟量控制
    """

    class Meta(RestFrameWorkModelSerializerTyping):
        model = MockControls
        exclude = ('devices', 'u_id')
        read_only_fields = ('u_id', 'devices')

    def update(self, instance: MockControls, validated_data):
        index = instance.number
        # 发送offer更改传感器
        validated_data['number'] = index
        res = TCPManager.get_instance().offer(
            instance.devices.device_serial_number,
            msg={
                'item': 'mock_output',
                'subitem': 'settings',
                'data': [validated_data],
                'index': index - 1
            },
            handle_type=3,
            time_out=settings.TASK_TIME_OUT
        )

        if not res['msg']['data']:
            raise Exception('update faild')
        return super().update(instance, validated_data)

    def create(self, validated_data):
        with transaction.atomic() as tr:
            dev: Devices = self.context['dev']
            queryset = MockControls.objects.select_for_update().filter(devices_id=dev.id)
            # 查询最大的number
            max_number = queryset.aggregate(max=Max('number'))['max']
            # 查询所有number
            numbers = queryset.values('number')
            index = self._get_not_use_number(max_number, numbers)
            # 发送offer更改传感器
            validated_data['number'] = index + 1
            validated_data['u_id'] = index + 1
            validated_data['is_use'] = True
            validated_data['devices_id'] = dev.id
            res = TCPManager.get_instance().offer(
                dev.device_serial_number,
                msg={
                    'item': 'mock_output',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': index
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )

            if not res:
                raise Exception('update faild')
            return super().create(validated_data)

    def _get_not_use_number(self, max_number: int, numbers: dict) -> int:
        """
        获取设备当中  未使用的number

        :param max_number:
        :param numbers:
        :return:
        """
        count = 0
        if len(numbers) == max_number:
            # 假如number中间没有空出的的number则立即返回
            count = max_number
        else:
            for i in numbers:

                """
                    从零开始遍历 number 假如中间删除了一个  
                    例如  numbers 为 [1,2,3,4,7]
                    则当count为4的时候就会跳出，拿到应该使用的number，  
                    后期优化可以使用set 中的 set.difference() 来查找
                """
                if i['number'] - 1 != count:
                    break

                count += 1

        return count


class DeviceVideoMonitorSerializer(serializers.ModelSerializer):
    class Meta:
        model = DeviceVideoMonitor
        fields = "__all__"


class MockSensorSerializer(serializers.ModelSerializer):

    data = serializers.SerializerMethodField()

    type = serializers.SerializerMethodField()

    def get_type(self, row):
        return 'mock'

    def get_data(self, row):
        request = self.context['request']
        limit = request.query_params.get("limit", '20')
        limit = int(limit)
        query = History.objects.filter(
            mock_sensor_id=row.id).order_by('time_ticket').all()
        if query.count() >= limit:
            data = query[query.count() - limit:]
        else:
            data = query
        return HistorySerialzer(instance=data, many=True, read_only=True).data

    class Meta(RestFrameWorkModelSerializerTyping):
        model = MockSensor
        fields = '__all__'
        read_only_fields = ('u_id', 'number', 'is_use', 'devices')

    def create(self, validated_data):
        with transaction.atomic() as tr:
            dev = self.context['dev']
            queryset = MockSensor.objects.filter(devices_id=dev.id)
            queryset.select_for_update()
            max_number = queryset.aggregate(max=Max('number'))
            numbers = queryset.values('number')
            index = self._get_not_use_number(max_number, numbers)

            validated_data['number'] = index + 1
            validated_data['u_id'] = index + 1
            validated_data['is_use'] = True
            validated_data['devices_id'] = dev.id
            res = TCPManager.get_instance().offer(
                dev.device_serial_number,
                msg={
                    'item': 'mock_input',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': index
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )

            if not res:
                raise Exception('create faild')
            instance = super().create(validated_data)
            request = self.context['request']
            UserActionLog.objects.create(
                user=request.user,
                devices=dev,
                description=f"新增了模拟量传感器{instance.name}"
            )

            return instance

    def update(self, instance: MockSensor, validated_data):
        with transaction.atomic() as tr:
            dev = self.context['dev']

            index = instance.number
            validated_data['number'] = index
            validated_data['u_id'] = index
            validated_data['is_use'] = True
            validated_data['devices_id'] = dev.id

            res = TCPManager.get_instance().offer(
                dev.device_serial_number,
                msg={
                    'item': 'mock_input',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': index - 1
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )

            if not res:
                raise Exception('update faild')
            request = self.context['request']
            UserActionLog.objects.create(
                user=request.user,
                devices=dev,
                description=f"修改了模拟量传感器{instance.name}"
            )
            return super().update(instance, validated_data)

    def _get_not_use_number(self, max_number: int, numbers: dict) -> int:
        """
        获取设备当中  未使用的number

        :param max_number:
        :param numbers:
        :return:
        """
        count = 0
        if len(numbers) == max_number:
            # 假如number中间没有空出的的number则立即返回
            count = max_number
        else:
            for i in numbers:

                """
                    从零开始遍历 number 假如中间删除了一个  
                    例如  numbers 为 [1,2,3,4,7]
                    则当count为4的时候就会跳出，拿到应该使用的number，  
                    后期优化可以使用set 中的 set.difference() 来查找
                """
                if i['number'] - 1 != count:
                    break

                count += 1

        return count


class NumberSensorSerializer(serializers.ModelSerializer):
    """
    数字量传感器
    """
    data = serializers.SerializerMethodField()

    type = serializers.SerializerMethodField()

    def get_type(self, row):
        return 'number'

    def get_data(self, row):
        request = self.context['request']
        limit = request.query_params.get("limit", '20')
        limit = int(limit)
        query = History.objects.filter(
            number_sensor_id=row.id).order_by('time_ticket').all()
        if query.count() >= limit:
            data = query[query.count() - limit:]
        else:
            data = query
        return HistorySerialzer(instance=data, many=True, read_only=True).data

    class Meta(RestFrameWorkModelSerializerTyping):
        model = NumberSensor
        fields = '__all__'
        read_only_fields = ('number', 'u_id', 'is_use', 'devices')

    def create(self, validated_data):
        with transaction.atomic() as tr:
            dev: Devices = self.context['dev']
            queryset = NumberSensor.objects.select_for_update().filter(devices_id=dev.id)
            # 查询最大的number
            max_number = queryset.aggregate(max=Max('number'))['max']
            # 查询所有number
            numbers = queryset.values('number')
            index = self._get_not_use_number(max_number, numbers)
            # 发送offer更改传感器
            validated_data['number'] = index + 1
            validated_data['u_id'] = index + 1
            validated_data['is_use'] = 1
            validated_data['start'] = 1
            validated_data['devices_id'] = dev.id
            res = TCPManager.get_instance().offer(
                dev.device_serial_number,
                msg={
                    'item': 'number_input',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': index
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )
            print(res)
            if not res or not res['msg']['data']:
                raise Exception('update faild')
            instance = super().create(validated_data)
            request = self.context['request']
            UserActionLog.objects.create(
                user=request.user,
                devices=dev,
                description=f"新建了数字量传感器{instance.name}"
            )

            return instance

    def update(self, instance: NumberSensor, validated_data):
        with transaction.atomic() as tr:
            dev: Devices = self.context['dev']
            index = instance.number - 1
            # 发送offer更改传感器
            validated_data['number'] = index + 1
            validated_data['u_id'] = index + 1
            validated_data['is_use'] = 1
            validated_data['start'] = 1
            validated_data['devices_id'] = dev.id
            res = TCPManager.get_instance().offer(
                dev.device_serial_number,
                msg={
                    'item': 'number_input',
                    'subitem': 'settings',
                    'data': [validated_data],
                    'index': index
                },
                handle_type=3,
                time_out=settings.TASK_TIME_OUT
            )

            if not res:
                raise Exception('update faild')
            request = self.context['request']
            UserActionLog.objects.create(
                user=request.user,
                devices=dev,
                description=f"修改了数字量传感器{instance.name}"
            )
            return super().update(instance, validated_data)

    def _get_not_use_number(self, max_number: int, numbers: dict) -> int:
        """
        获取设备当中  未使用的number

        :param max_number:
        :param numbers:
        :return:
        """
        count = 0
        if len(numbers) == max_number:
            # 假如number中间没有空出的的number则立即返回
            count = max_number
        else:
            for i in numbers:

                """
                    从零开始遍历 number 假如中间删除了一个  
                    例如  numbers 为 [1,2,3,4,7]
                    则当count为4的时候就会跳出，拿到应该使用的number，  
                    后期优化可以使用set 中的 set.difference() 来查找
                """
                if i['number'] - 1 != count:
                    break

                count += 1

        return count
